﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using Dycox.ComponentModel;
using Dycox.Linq;
using Newtonsoft.Json;

namespace Dycox.Workflows
{
    partial class WFStep : IWorkflowComponent, ICloneable
    {

        public MessageSet Hints
        {
            get
            {
                string data = _HintData;

                if (!string.IsNullOrEmpty(data))
                    return JsonConvert.DeserializeObject<MessageSet>(data);
                else
                    return null;
            }
            set
            {
                if (value == null)
                    HintData = null;
                else
                    HintData = JsonConvert.SerializeObject(value);
            }
        }

        //public static WFStep[] GetActiveSteps(string workflowName, string stageName)
        //{
        //    return QuerySteps(workflowName, stageName, new WorkflowComponentStatus[] { WorkflowComponentStatus.Active }, null, true);
        //}

        //public static ComponentIdentifier[] GetActiveStepsID(string workflowName, string stageName)
        //{
        //    using (var provider = DataProvider.CreateProvider())
        //    {
        //        return CreateStepQuery(provider.WFSteps, workflowName, stageName,
        //            new WorkflowComponentStatus[] { WorkflowComponentStatus.Active }, null)
        //            .Select(o => new ComponentIdentifier(o.ProcessId, o.ThreadId, o.Sequence))
        //            .ToArray();
        //    }
        //}

        internal static WFStep[] QuerySteps(ComponentQueryArguments args, PagingInfo paging)
        {
            using (var provider = args.CreateProvider())
            {
                provider.InitializeStepQuery(args);

                IQueryable<WFStep> q = ParseQuery(provider.WFSteps, args);
                
                q = q.Page(paging);

                return q.ToArray();
            }
        }

        internal static IQueryable<WFStep> ParseQuery(IQueryable<WFStep> source, ComponentQueryArguments args)
        {
            IQueryable<WFStep> q = source;
            int count;

            if (args != null)
            {
                if (args.WorkflowName != null)
                    q = q.Where(o => o.Thread.Process.WorkflowName == args.WorkflowName);

                if (args.IsActive.HasValue)
                {
                    bool active = args.IsActive.Value;

                    if (active)
                        q = q.Where(o => o.IsActive);
                    else
                        q = q.Where(o => !o.IsActive);
                }

                if ((count = args.ComponentIDCount) > 0)
                {
                    if (count == 1)
                    {
                        ComponentIdentifier id = args.ComponentIDs[0];
                        q = q.Where(o => o.ProcessId == id.ProcessId);
                        if (id.ThreadId != null)
                        {
                            q = q.Where(o => o.ThreadId == id.ThreadId.Value);
                            if (id.StepSequence != null)
                                q = q.Where(o => o.Sequence == id.StepSequence.Value);
                        }
                    }
                    else
                    {
                        var array = args.ComponentIDs.Select(o => o.ProcessId);
                        q = q.Where(o => array.Contains(o.ProcessId));
                        //throw new NotSupportedException("At most ONE ID supported.");
                    }

                }

                if ((count = args.StageNameCount) > 0)
                {
                    if (count == 1)
                        q = q.Where(o => o.StageName == args.StageNames[0]);
                    else
                        q = q.Where(o => args.StageNames.Contains(o.StageName));
                }

                if ((count = args.StatusCount) > 0)
                {
                    if (count == 1)
                    {
                        q = q.Where(o => o.Status == args.Status[0]);
                    }
                    else
                        q = q.Where(o => args.Status.Contains(o.Status));
                }

                if (!args.CreationTime.IsEmpty)
                {
                    DateRange range = args.CreationTime;

                    //if (range.IsLowerSpecified) q = q.Where(o => o.CreationTime >= range.LowerBound);
                    //if (range.IsUpperSpecified) q = q.Where(o => o.CreationTime <= range.UpperBound);
                    q = q.InRange(o => o.CreationTime, range);
                }

                if (!args.FinishTime.IsEmpty)
                {
                    DateRange range = args.FinishTime;
                    q = q.InRange(o => o.FinishTime, range);
                }

                if (!args.Duration.IsEmpty)
                {
                    DateTime now = WorkflowManager.TimeService.Now;
                    q = q.InRange(o => ((o.FinishTime ?? now) - o.CreationTime), args.Duration );
                }

                q = QueryExecutor.ApplySortOrder<WFStep>(q, args.Sort);
            }

            return q;
        }

        internal StageContract GetUserState(Stage stage)
        {
            return DeserializeContract(stage.ContractType, _Data);

        }

        //public StageStateContract GetContract()
        //{
        //    if (_Data == null) return null;
        //    else return WorkflowManager.GetStage(this)
        //}

        public WFStep FindNearestStep()
        {
            return FindNearestStep(null);
        }

        public WFStep FindNearestStep(string stageName)
        {
            WFStep step;

            try
            {
                step = WorkflowManager.GetWorkflow(this).Control.GetNearestCompletedStep(this, stageName);
            }
            catch (ObjectDisposedException)
            {
                using (var dp = DataProvider.CreateProvider())
                {
                    step = (WFStep)dp.GetComponent(this.ID, false);
                    if (step != null)
                        step = WorkflowManager.GetWorkflow(step).Control.GetNearestCompletedStep(step, stageName);
                }
                
            }

            return step;
        }

        internal void SetUserState(StageContract obj)
        {
            Data = SerializeContract(obj);
        }

        internal static StageContract DeserializeContract(Type type, string json)
        {
            if (string.IsNullOrEmpty(json))
                return null;

            return (StageContract)JsonConvert.DeserializeObject(json, type);
        }

        internal static string SerializeContract(StageContract contract)
        {
            if (contract == null) return null;

            JsonSerializerSettings js = new JsonSerializerSettings();
            js.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;

            return JsonConvert.SerializeObject(contract, js);

        }

        public ComponentIdentifier ID
        {
            get { return new ComponentIdentifier(_ProcessId, _ThreadId, _Sequence); }
        }

        IWorkflowComponent IWorkflowComponent.OwnerComponent
        {
            get { return this.Thread; }
        }

        public WFStep Clone()
        {
            WFStep step = (WFStep)this.MemberwiseClone();
            step.Initialize();
            return step;
        }

        object ICloneable.Clone()
        {
            return this.Clone();
        }
    }
}
